package com.fm.demo.jpush

import android.content.Context
import android.util.SparseArray
import cn.jpush.android.api.JPushInterface
import cn.jpush.android.api.JPushMessage
import com.blankj.utilcode.util.LogUtils
import com.blankj.utilcode.util.NetworkUtils
import com.blankj.utilcode.util.ToastUtils
import com.fm.demo.bean.TagAliasBean
import com.fm.demo.utils.CommonUtils
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.util.*

/**
 * 处理tag alias相关的逻辑
 */
class TagAliasOperatorHelper private constructor() {
    private val setActionCache = SparseArray<Any>()

    companion object {
        private const val TAG = "JIGUANG-TagAliasHelper"
        var sequence = 1
        /**增加 */
        const val ACTION_ADD = 1
        /**覆盖 */
        const val ACTION_SET = 2
        /**删除部分 */
        const val ACTION_DELETE = 3
        /**删除所有 */
        const val ACTION_CLEAN = 4
        /**查询 */
        const val ACTION_GET = 5

        const val ACTION_CHECK = 6

        val instance: TagAliasOperatorHelper by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
            TagAliasOperatorHelper()
        }
    }

    fun onTagOperatorResult(context: Context, jPushMessage: JPushMessage) {
        val sequence = jPushMessage.sequence
        LogUtils.iTag(TAG, "action - onTagOperatorResult, sequence:" + sequence + ",tags:" + jPushMessage.tags)
        //根据sequence从之前操作缓存中获取缓存记录
        val tagAliasBean = setActionCache.get(sequence)
        if (tagAliasBean == null) {
            LogUtils.eTag(TAG, "获取缓存记录失败")
            return
        }
        tagAliasBean as TagAliasBean
        if (jPushMessage.errorCode == 0) {
            LogUtils.iTag(TAG, "action - modify tag Success,sequence:$sequence")
            setActionCache.remove(sequence)
            CommonUtils.showToastShotMain("标签 操作成功")
        } else {
            var logs = "Failed to " + getActionStr(tagAliasBean.action) + " tags"
            if (jPushMessage.errorCode == 6018) {
                //tag数量超过限制,需要先清除一部分再add
                logs += ", tags is exceed limit need to clean"
            }
            logs += ", errorCode:" + jPushMessage.errorCode
            if (!retryActionIfNeeded(context, jPushMessage.errorCode, tagAliasBean)) {
                LogUtils.eTag(TAG, logs)
            }
        }
    }

    fun onCheckTagOperatorResult(context: Context, jPushMessage: JPushMessage) {
        val sequence = jPushMessage.sequence
        LogUtils.iTag(TAG, "action - onCheckTagOperatorResult, sequence:" + sequence + ",checktag:" + jPushMessage.checkTag)
        //根据sequence从之前操作缓存中获取缓存记录
        val tagAliasBean = setActionCache.get(sequence)
        if (tagAliasBean == null) {
            LogUtils.eTag(TAG, "获取缓存记录失败")
            return
        }
        tagAliasBean as TagAliasBean
        if (jPushMessage.errorCode == 0) {
            LogUtils.iTag(TAG, "tagBean:$tagAliasBean")
            setActionCache.remove(sequence)

            CommonUtils.showToastShotMain("标签 查询成功")
        } else {
            val logs = "Failed to " + getActionStr(tagAliasBean.action) + " tags, errorCode:" + jPushMessage.errorCode
            if (!retryActionIfNeeded(context, jPushMessage.errorCode, tagAliasBean)) {
                LogUtils.eTag(TAG, logs)
            }
        }
    }

    fun onAliasOperatorResult(context: Context, jPushMessage: JPushMessage) {
        val sequence = jPushMessage.sequence
        LogUtils.iTag(TAG, "action - onAliasOperatorResult, sequence:" + sequence + ",alias:" + jPushMessage.alias)
        //根据sequence从之前操作缓存中获取缓存记录
        val tagAliasBean = setActionCache.get(sequence)
        if (tagAliasBean == null) {
            LogUtils.eTag(TAG, "获取缓存记录失败")
            return
        }
        tagAliasBean as TagAliasBean
        if (jPushMessage.errorCode == 0) {
            LogUtils.iTag(TAG, "action - modify alias Success,sequence:$sequence")
            setActionCache.remove(sequence)

            CommonUtils.showToastShotMain("别名 操作成功")
        } else {
            val logs = "Failed to " + getActionStr(tagAliasBean.action) + " alias, errorCode:" + jPushMessage.errorCode
            if (!retryActionIfNeeded(context, jPushMessage.errorCode, tagAliasBean)) {
                LogUtils.eTag(TAG, logs)
            }
        }
    }

    fun onMobileNumberOperatorResult(context: Context, jPushMessage: JPushMessage) {
        val sequence = jPushMessage.sequence
        LogUtils.iTag(TAG, "action - onMobileNumberOperatorResult, sequence:" + sequence + ",mobileNumber:" + jPushMessage.mobileNumber)
        if (jPushMessage.errorCode == 0) {
            LogUtils.iTag(TAG, "action - set mobile number Success,sequence:$sequence")
            setActionCache.remove(sequence)

            CommonUtils.showToastShotMain("手机号 操作成功")
        } else {
            val logs = "Failed to set mobile number, errorCode:" + jPushMessage.errorCode
            if (!retrySetMobileNumberActionIfNeeded(context, jPushMessage.errorCode, jPushMessage.mobileNumber)) {
                LogUtils.eTag(TAG, logs)
            }
        }
    }

    /**
     * 处理设置tag
     */
    fun handleAction(context: Context, sequence: Int, tagAliasBean: TagAliasBean) {
        setActionCache.put(sequence, tagAliasBean)
        if (tagAliasBean.isAliasAction) {
            when (tagAliasBean.action) {
                ACTION_GET -> JPushInterface.getAlias(context, sequence)
                ACTION_DELETE -> JPushInterface.deleteAlias(context, sequence)
                ACTION_SET -> JPushInterface.setAlias(context, sequence, tagAliasBean.alias)
                else -> {
                    LogUtils.wTag(TAG, "unsupport alias action type")
                    return
                }
            }
        } else {
            tagAliasBean.tags.let {
                when (tagAliasBean.action) {
                    ACTION_ADD -> JPushInterface.addTags(context, sequence, it)
                    ACTION_SET -> JPushInterface.setTags(context, sequence, it)
                    ACTION_DELETE -> JPushInterface.deleteTags(context, sequence, it)
                    ACTION_CHECK -> {
                        //一次只能check一个tag
                        JPushInterface.checkTagBindState(context, sequence, it.toTypedArray()[0])
                    }
                    ACTION_GET -> JPushInterface.getAllTags(context, sequence)
                    ACTION_CLEAN -> JPushInterface.cleanTags(context, sequence)
                    else -> {
                        LogUtils.wTag(TAG, "unsupport tag action type")
                        return
                    }
                }
            }

        }
    }


    private fun getRetryStr(isAliasAction: Boolean, actionType: Int, errorCode: Int): String {
        var str = "Failed to %s %s due to %s. Try again after 60s."
        str = String.format(
            Locale.ENGLISH,
            str,
            getActionStr(actionType),
            if (isAliasAction) "alias" else " tags",
            if (errorCode == 6002) "timeout" else "server too busy"
        )
        return str
    }

    private fun getActionStr(actionType: Int): String {
        when (actionType) {
            ACTION_ADD -> return "add"
            ACTION_SET -> return "set"
            ACTION_DELETE -> return "delete"
            ACTION_GET -> return "get"
            ACTION_CLEAN -> return "clean"
            ACTION_CHECK -> return "check"
        }
        return "unkonw operation"
    }


    private fun retryActionIfNeeded(context: Context, errorCode: Int, tagAliasBean: TagAliasBean): Boolean {
        if (!NetworkUtils.isConnected()) {
            LogUtils.wTag(TAG, "no network")
            return false
        }
        //返回的错误码为6002 超时,6014 服务器繁忙,都建议延迟重试
        if (errorCode == 6002 || errorCode == 6014) {
            LogUtils.dTag(TAG, "need retry")
            GlobalScope.launch {
                LogUtils.iTag(TAG, "on delay time")
                delay(1000 * 60)
                sequence++
                handleAction(context, sequence, tagAliasBean)
            }
            val logs = getRetryStr(tagAliasBean.isAliasAction, tagAliasBean.action, errorCode)
            ToastUtils.showShort(logs)
            return true
        }
        return false
    }


    private fun retrySetMobileNumberActionIfNeeded(context: Context, errorCode: Int, mobileNumber: String): Boolean {
        if (!NetworkUtils.isConnected()) {
            LogUtils.wTag(TAG, "no network")
            return false
        }
        //返回的错误码为6002 超时,6024 服务器内部错误,建议稍后重试
        if (errorCode == 6002 || errorCode == 6024) {
            LogUtils.iTag(TAG, "retry set mobile number")
            sequence++
            LogUtils.dTag(TAG, "sequence:$sequence,mobileNumber:$mobileNumber")
            GlobalScope.launch {
                delay(1000 * 60)
                JPushInterface.setMobileNumber(context, sequence, mobileNumber)
            }
            var str = "Failed to set mobile number due to %s. Try again after 60s."
            str = String.format(Locale.ENGLISH, str, if (errorCode == 6002) "timeout" else "server internal error”")
            LogUtils.iTag(TAG, str)
            return true
        }
        return false
    }

}
